package com.william.jdbcplus.boot.starter.wrap;

import com.william.jdbcplus.boot.starter.util.DbUtil;
import com.william.jdbcplus.common.enums.DbType;
import com.william.jdbcplus.common.page.IPage;
import com.william.jdbcplus.common.page.Page;
import com.william.jdbcplus.core.condition.query.Query;
import com.william.jdbcplus.core.condition.segment.MergeSegments;
import com.william.jdbcplus.core.condition.wrap.AbstractWrap;
import com.william.jdbcplus.core.dialect.pagination.PaginationFactory;
import com.william.jdbcplus.core.toolkit.MapTransformUtils;
import com.william.jdbcplus.core.util.JdbcUtil;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 通用SQL查询封装
 *
 * @author niliwei
 */
public class QueryBootWrap extends AbstractWrap<String, QueryBootWrap> implements Query {
    private QueryLambdaBootWrap lambdaWrapper = null;

    public QueryBootWrap() {
        super.initNeed();
    }

    /**
     * 返回一个支持 lambda 函数写法的 wrapper
     */
    public QueryLambdaBootWrap lambda() {
        if (lambdaWrapper == null) {
            lambdaWrapper = new QueryLambdaBootWrap(paramNameValuePairs, expression);
        }
        return lambdaWrapper;
    }

    /**
     * 非对外公开的构造方法,只用于生产嵌套 sql
     */
    private QueryBootWrap(Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments) {
        this.paramNameValuePairs = paramNameValuePairs;
        this.expression = mergeSegments;
    }


    @Override
    protected QueryBootWrap instance() {
        return new QueryBootWrap(paramNameValuePairs, new MergeSegments());
    }

    @Override
    public void clear() {
        lambdaWrapper = null;
        initNeed();
    }

    @Override
    public List<Map<String, Object>> queryListMap() {
        NamedParameterJdbcTemplate jdbcOpt = getTemplate();
        String sql = getAll();
        Map<String, Object> paramMap = this.getParamNameValuePairs();
        List<Map<String, Object>> listMap = jdbcOpt.queryForList(sql, paramMap);
        return listMap;
    }

    @Override
    public Map<String, Object> queryMap() {
        List<Map<String, Object>> list = queryListMap();
        return getFirst(list);
    }

    @Override
    public IPage<Map<String, Object>> queryPageMap(IPage<?> page) {
        NamedParameterJdbcTemplate jdbcOpt = getTemplate();
        DbType dbType = JdbcUtil.getDbType(jdbcOpt.getJdbcTemplate().getDataSource());
        Integer totalCount = queryCount();
        List<Map<String, Object>> listMap = null;
        if (totalCount <= 0) {
            listMap = new ArrayList<>();
        } else {
            String sql = getAll();
            String sqlPage = PaginationFactory.getPaginationSql(page, sql, dbType);
            listMap = jdbcOpt.queryForList(sqlPage, this.getParamNameValuePairs());
        }

        Page<Map<String, Object>> resultPage = new Page<>(page.getCurrent(), page.getSize());
        resultPage.setRecords(listMap);
        resultPage.setTotal(totalCount);
        return resultPage;
    }

    @Override
    public Integer queryCount() {
        NamedParameterJdbcTemplate jdbcOpt = getTemplate();
        String sqlCount = getCount();
        Map<String, Object> paramMap = this.getParamNameValuePairs();
        Integer count = jdbcOpt.queryForObject(sqlCount, paramMap, Integer.class);
        return count;
    }

    @Override
    public <M> M queryOne(Class<M> clazz) {
        return getFirst(queryList(clazz));
    }

    @Override
    public <M> List<M> queryList(Class<M> clazz) {
        List<Map<String, Object>> listMap = queryListMap();
        List<M> resultList = MapTransformUtils.mapToEntityList(listMap, clazz);
        return resultList;
    }

    @Override
    public <M> Page<M> queryPage(IPage<?> page, Class<M> clazz) {
        IPage<Map<String, Object>> mapPage = queryPageMap(page);
        List<Map<String, Object>> mapList = mapPage.getRecords();
        List<M> resultRows = MapTransformUtils.mapToEntityList(mapList, clazz);
        Page<M> pager = new Page<>(page.getCurrent(), page.getSize());
        pager.setTotal(mapPage.getTotal());
        pager.setRecords(resultRows);
        return pager;
    }

    private NamedParameterJdbcTemplate getTemplate() {
        return DbUtil.namedParameterJdbcTemplate;
    }
}
